package edu.pku.sei.mte.compositetrans.pkumt;

import java.io.File;

import org.eclipse.emf.ecore.resource.Resource;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import agg.xt_basis.GraGra;
import agg.xt_basis.TypeException;

import edu.pku.sei.mte.compositetrans.TResult;
import edu.pku.sei.mte.compositetrans.UniTController;
import edu.pku.sei.mte.compositetrans.configuration.MetaModelSpec;
import edu.pku.sei.mte.compositetrans.configuration.ModelSpec;
import edu.pku.sei.mte.compositetrans.configuration.TransDescriptor;
import edu.pku.sei.mte.exceptions.ExceptionRegistry;
import edu.pku.sei.mte.mtemodel.model.MModelGraph;
import edu.pku.sei.mte.mtemodel.mtmodel.ModelType;
import edu.pku.sei.mte.mtemodel.mtmodel.rulebased.RBTransformation;
import edu.pku.sei.mte.runtime.rulebased.RBTEnvironment;
import edu.pku.sei.mte.serialization.EcoreLoader;
import edu.pku.sei.mte.serialization.EcoreTransformationLoader;
import edu.pku.sei.mte.serialization.EcoreXMILoader;
import edu.pku.sei.mte.serialization.EcoreXMISaver;
import edu.pku.sei.mte.serialization.ExceptionRegistryLoader;
import edu.pku.sei.mte.transformation.ModelTransformationVisitor;
import edu.pku.sei.mte.transformation.VisitorFactory;
import edu.pku.sei.mte.utilities.ModelFormat;
import edu.pku.sei.mte.utilities.Utility;

public class PKUMTController extends UniTController {

	public static final int QVT = 0;
	public static final int GT = 1;
	
	private RBTransformation trans = null;
	
	private RBTEnvironment env = null;
	
	private String targetFileName = null;
	
	private String direction = null;
	
	public PKUMTController(Element root) {
		super(ModelFormat.PKUMT, root);
		env = new RBTEnvironment();
		Utility.cur_env = env;
	}

	@Override
	public boolean launch() throws Exception {
		long start = System.currentTimeMillis();
		env.getTransformation().accept(env.getMtVisitor());
		System.out.println("Time: "+ (System.currentTimeMillis() - start));
		
		result.isSucceed = true;
		result.targetModelType = env.getTarget();
		result.targetModel = env.getModelGraph(env.getTarget());
		return true;
	}

	@Override
	protected boolean init() {
		String semantic = tconf.props.getProperty("control");
		String mode = tconf.props.getProperty("mtmode");
		ModelTransformationVisitor mtv = VisitorFactory.INSTANCE.getModelTransformationVisitor(semantic, mode, env);
		env.setMtVisitor(mtv);
		for(MetaModelSpec mms : tconf.metaModels){
			loadModelType(mms.name, mms.filePath);
		}
		String transFileName = tconf.props.getProperty("file");
		loadTransformation(transFileName);
		direction = tconf.props.getProperty("direction");
		String exceptionFileName = tconf.props.getProperty("exceptionfile");
		if(exceptionFileName != null && !exceptionFileName.equals(""))
			loadExceptionRegistry(exceptionFileName);
		return true;
	}

	@Override
	protected boolean loadModel(TResult result, ModelSpec ms) {
		if(result.modelFormat == ModelFormat.GGX){
			EcoreLoader el = EcoreLoader.getEcoreLoader(ms.typeName);
			try {
				ms.model = Utility.GGX2PKUMT((GraGra) result.targetModel, el.load());
			} catch (TypeException e) {
				e.printStackTrace();
				return false;
			}
		}
		else if(result.modelFormat == ModelFormat.PKUMT){
			ms.model = result.targetModel;
		}else if(result.modelFormat == ModelFormat.EMF){
			ms.filePath = ((Resource)result.targetModel).getURI().toFileString();
			loadModel(ms);
			return true;
		}else return false;
		return true;
	}

	@Override
	protected boolean loadModel(ModelSpec ms) {
		if(direction.equals(ms.name)){
			targetFileName = ms.filePath;
			ms.model = loadModel(ms.typeName, ms.filePath, true);
		}else{
			ms.model = loadModel(ms.typeName, ms.filePath, false);
		}
		if(ms.model == null)
			return false;
		return true;
	}

	@Override
	protected boolean saveModel() {
		if(targetFileName != null && !targetFileName.equals("")){
			EcoreXMISaver saver = new EcoreXMISaver();
			saver.save(env.getModelGraph(env.getTarget()), targetFileName);
			return true;
		}else return false;
	}

	public void loadModelType(String typeName, String typeFileName){
		EcoreLoader loader = EcoreLoader.getEcoreLoader(typeName, typeFileName);
		loader.load();
	}
	
	public MModelGraph loadModel(String typeName, String modelFileName, boolean isTarget){
		if(trans == null){
			System.out.println("please load transformation first");
			return null;
		}
		ModelType modelType = trans.getModelType(typeName);
		if(modelType == null){
			System.out.println("no " + typeName + " in transformation");
			return null;
		}
		MModelGraph model = null;
		if(modelFileName != null && !modelFileName.equals("")){
			EcoreXMILoader loader = new EcoreXMILoader();
			model = loader.load(EcoreLoader.getEcoreLoader(modelType.getTypeGraph()), modelFileName);
		}
		if(model == null){
			model = new MModelGraph();
			model.setTypeGraph(modelType.getTypeGraph());
		} else if(!model.getTypeGraph().equals(modelType.getTypeGraph())){
			System.out.println("typeGraphs are not match ");
			return null;
		}
		env.getModelCollection().put(modelType, model);
		if(isTarget){
			env.setTarget(modelType);
		}
		return model;
	}
	
	public void loadTransformation(String filename){
		File file = new File(filename);
		if(!file.exists()){
			System.out.println("file not exist!");
			return;
		}
		EcoreTransformationLoader transLoader = new EcoreTransformationLoader();
		try {
			trans = transLoader.load(file);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		env.setTransformation(trans);
	}

	public void loadExceptionRegistry(String filename){
		ExceptionRegistryLoader loader = new ExceptionRegistryLoader();
		ExceptionRegistry reg = loader.load(filename, trans);
		env.getExhandler().registerExceptions(reg);
	}
	
	public void saveTargetModel(String filename){
		if(filename != null && !filename.equals(""))
			targetFileName = filename;
		if(targetFileName != null && !targetFileName.equals("")){
			EcoreXMISaver saver = new EcoreXMISaver();
			saver.save(env.getModelGraph(env.getTarget()), targetFileName);
		}
	}
	
	public RBTransformation getTrans() {
		return trans;
	}

	public RBTEnvironment getEnv() {
		return env;
	}
	
	public void load(String filename){
		load(Utility.loadXML(filename));
	}

	public void load(Element root) {
		String semantic = root.getAttribute("control").toLowerCase();
		String mode = root.getAttribute("mtmode").toLowerCase();
		ModelTransformationVisitor mtv = VisitorFactory.INSTANCE.getModelTransformationVisitor(semantic, mode, env);
		env.setMtVisitor(mtv);
		NodeList types = root.getElementsByTagName("type");
		int length = types.getLength();
		for(int i=0;i<length;i++){
			Node n = types.item(i);
			if(n.getNodeType()!=Node.ELEMENT_NODE)
				continue;
			Element type = (Element) n;
			String typeName = type.getAttribute("name");
			String typeFileName = type.getAttribute("file");
			loadModelType(typeName, typeFileName);
		}
		String transFileName = root.getAttribute("file");
		loadTransformation(transFileName);
		targetFileName = root.getAttribute("target");
		NodeList models = root.getElementsByTagName("model");
		length = models.getLength();
		for(int i=0;i<length;i++){
			Node n = models.item(i);
			if(n.getNodeType()!=Node.ELEMENT_NODE)
				continue;
			Element model = (Element) n;
			String type = model.getAttribute("type");
			String modelFileName = model.getAttribute("file");
			String target = model.getAttribute("isTarget");
			boolean isTarget = false;
			if(target != null && target.equals("true"))
				isTarget = true;
			loadModel(type, modelFileName, isTarget);
		}
		String exceptionFileName = root.getAttribute("exceptionfile");
		loadExceptionRegistry(exceptionFileName);
	}
	
	public void load(TransDescriptor tc) {
		String semantic = tc.props.getProperty("control");
		String mode = tc.props.getProperty("mtmode");
		ModelTransformationVisitor mtv = VisitorFactory.INSTANCE.getModelTransformationVisitor(semantic, mode, env);
		env.setMtVisitor(mtv);
		for(MetaModelSpec mms : tc.metaModels){
			loadModelType(mms.name, mms.filePath);
		}
		String transFileName = tc.props.getProperty("file");
		loadTransformation(transFileName);
		String target = tc.props.getProperty("direction");
		for(ModelSpec ms : tc.models){
			boolean isTarget = false;
			if(target.equals(ms.name)){
				isTarget = true;
				targetFileName = ms.filePath;
			}
			loadModel(ms.typeName, ms.filePath, isTarget);
		}
		String exceptionFileName = tc.props.getProperty("exceptionfile");
		if(exceptionFileName != null && !exceptionFileName.equals(""))
			loadExceptionRegistry(exceptionFileName);
	}
	
	public static void main(String[] args) throws Exception {
		PKUMTController c = new PKUMTController(Utility.loadXML("UMLtoRDBMS\\UMLtoRDBMS.xml"));
		c.transform();
	}
}
